﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using KNXObjects;
using tuwien.auto.eibclient;
using KNXObjects.Types;

namespace KNXConnector.EIBD
{
    internal class ValueConverter : IValueConverter
    {
        public KNXObjects.ObjectBase FillObjectValue(KNXObjects.ObjectBase knxObject, tuwien.auto.eibclient.EIBBuffer buffer)
        {
            Type objType = knxObject.GetType();
            if (objType == typeof(SwitchingObject))
                FillSwitchingObjectValue(knxObject, buffer);
            else if (objType == typeof(DimmingObject))
                FillStepDirObjectValue(knxObject, buffer);
            else if (objType == typeof(BlindObject))
                FillStepDirObjectValue(knxObject, buffer);
            else if (objType == typeof(DateObject))
                FillDateObjectValue(knxObject, buffer);
            else if (objType == typeof(HeatingModeObject)
                  || objType == typeof(AngleObject)
                  || objType == typeof(ScaledObject))
                FillUnsignedIntValue(knxObject, buffer);
            else if (objType == typeof(S8Object))
                FillS8ObjectValue(knxObject, buffer);
            else if (objType == typeof(S16Object))
                FillS16ObjectValue(knxObject, buffer);
            else if (objType == typeof(S32Object))
                FillS32ObjectValue(knxObject, buffer);
            else if (objType == typeof(StringObject))
                FillStringObjectValue(knxObject, buffer);
            else if (objType == typeof(U16Object))
                FillU16ObjectValue(knxObject, buffer);
            else if (objType == typeof(U32Object))
                FillU32ObjectValue(knxObject, buffer);
            else if (objType == typeof(ValueObject))
                FillValueObjectValue(knxObject, buffer);
            else if (objType == typeof(ValueObject32))
                FillValueObject32Value(knxObject, buffer);

            return knxObject;
        }

        public EIBBuffer FillBuffer(ObjectBase knxObject, EIBBuffer buffer)
        {
            Type objType = knxObject.GetType();
            if (objType == typeof(SwitchingObject))
                FillSwitchingObjectBuffer(knxObject, buffer);
            else if (objType == typeof(DimmingObject))
                FillStepDirObjectBuffer(knxObject, buffer);
            else if (objType == typeof(BlindObject))
                FillStepDirObjectBuffer(knxObject, buffer);
            else if (objType == typeof(DateObject))
                FillDateObjectBuffer(knxObject, buffer);
            else if (objType == typeof(HeatingModeObject)
                  || objType == typeof(AngleObject)
                  || objType == typeof(ScaledObject))
                FillUnsignedIntBuffer(knxObject, buffer);
            else if (objType == typeof(S8Object))
                FillS8ObjectBuffer(knxObject, buffer);
            else if (objType == typeof(S16Object))
                FillS16ObjectBuffer(knxObject, buffer);
            else if (objType == typeof(S32Object))
                FillS32ObjectBuffer(knxObject, buffer);
            else if (objType == typeof(StringObject))
                FillStringObjectBuffer(knxObject, buffer);
            else if (objType == typeof(U16Object))
                FillU16ObjectBuffer(knxObject, buffer);
            else if (objType == typeof(U32Object))
                FillU32ObjectBuffer(knxObject, buffer);
            else if (objType == typeof(ValueObject))
                FillValueObjectBuffer(knxObject, buffer);
            else if (objType == typeof(ValueObject32))
                FillValueObject32Buffer(knxObject, buffer);

            return buffer;
        }

        #region Fill buffer methods

        private void FillValueObject32Buffer(ObjectBase knxObject, EIBBuffer buffer)
        {
            throw new NotImplementedException();
        }

        private void FillValueObjectBuffer(ObjectBase knxObject, EIBBuffer buffer)
        {
            throw new NotImplementedException();
        }

        private void FillU32ObjectBuffer(ObjectBase knxObject, EIBBuffer buffer)
        {
            throw new NotImplementedException();
        }

        private void FillU16ObjectBuffer(ObjectBase knxObject, EIBBuffer buffer)
        {
            throw new NotImplementedException();
        }

        private void FillStringObjectBuffer(ObjectBase knxObject, EIBBuffer buffer)
        {
            throw new NotImplementedException();
        }

        private void FillS32ObjectBuffer(ObjectBase knxObject, EIBBuffer buffer)
        {
            throw new NotImplementedException();
        }

        private void FillS16ObjectBuffer(ObjectBase knxObject, EIBBuffer buffer)
        {
            throw new NotImplementedException();
        }

        private void FillS8ObjectBuffer(ObjectBase knxObject, EIBBuffer buffer)
        {
            throw new NotImplementedException();
        }

        private void FillUnsignedIntBuffer(ObjectBase knxObject, EIBBuffer buffer)
        {
            throw new NotImplementedException();
        }

        private void FillDateObjectBuffer(ObjectBase knxObject, EIBBuffer buffer)
        {
            throw new NotImplementedException();
        }

        private void FillStepDirObjectBuffer(ObjectBase knxObject, EIBBuffer buffer)
        {
            buffer.data[1] = Convert.ToByte(buffer.data[1] | ((knxObject.GetValue<StepDirType>().Direction == 1 ? 8 : 0) | (knxObject.GetValue<StepDirType>().StepCode & 0x07)));
        }

        private void FillSwitchingObjectBuffer(ObjectBase knxObject, EIBBuffer buffer)
        {
            buffer.data[1] = Convert.ToByte(buffer.data[1] | (knxObject.GetValue<bool>() ? 1 : 0));
        }

        #endregion

        #region Fill Object Methods

        private void FillValueObject32Value(ObjectBase sourceObject, tuwien.auto.eibclient.EIBBuffer buffer)
        {
            GenericObjectBase<double> castObject = sourceObject as GenericObjectBase<double>;

            if (buffer.data.Length < 6)
            {
                ///TODO
                //CustomLogger.WriteErrorEntry("Invalid packet received for ValueObject32 (too short)");
                return;
            }
            castObject.Value = Convert.ToDouble(buffer.data[2] << 24 | buffer.data[3] << 16 | buffer.data[4] << 8 | buffer.data[5]);

        }

        private void FillValueObjectValue(ObjectBase sourceObject, tuwien.auto.eibclient.EIBBuffer buffer)
        {
            GenericObjectBase<double> castObject = sourceObject as GenericObjectBase<double>;
            if (buffer.data.Length < 4)
            {
                ///TODO
                //CustomLogger.WriteErrorEntry("Invalid packet received for ValueObject (too short)");
                return;
            }
            double newValue;
            int d1 = buffer.data[2] * 256 + buffer.data[3];
            int m = d1 & 0x7ff;
            if ((d1 & 0x8000) == 0x8000)
                m |= ~0x7ff;
            int ex = (d1 & 0x7800) >> 11;
            newValue = ((double)m * (1 << ex) / 100);
            castObject.Value = newValue;
        }

        private void FillU32ObjectValue(ObjectBase sourceObject, tuwien.auto.eibclient.EIBBuffer buffer)
        {
            GenericObjectBase<uint> castObject = sourceObject as GenericObjectBase<uint>;
            castObject.Value = (uint)((buffer.data[2] << 24) | (buffer.data[3] << 16) | (buffer.data[4] << 8) | buffer.data[5]);
        }

        private void FillU16ObjectValue(ObjectBase sourceObject, tuwien.auto.eibclient.EIBBuffer buffer)
        {
            GenericObjectBase<uint> castObject = sourceObject as GenericObjectBase<uint>;
            uint newValue;
            if (buffer.data.Length == 4)
            {
                newValue = (uint)((buffer.data[2] << 8) | buffer.data[3]);
                castObject.Value = newValue;
            }
            else
                throw new ApplicationException("U16ObjectValue: wrong packet received (too short)");

        }

        private void FillStringObjectValue(ObjectBase sourceObject, tuwien.auto.eibclient.EIBBuffer buffer)
        {
            GenericObjectBase<StringBuilder> castObject = sourceObject as GenericObjectBase<StringBuilder>;
            if (buffer.data.Length < 2)
            {
                ///TODO
                //CustomLogger.WriteErrorEntry("Invalid packet received for StringObjectValue (too short)");
                return;
            }
            castObject.Value = new StringBuilder(Encoding.ASCII.GetString(buffer.data, 2, buffer.data.Length - 2));            
        }

        private void FillS8ObjectValue(ObjectBase sourceObject, tuwien.auto.eibclient.EIBBuffer buffer)
        {
            GenericObjectBase<int> castObject = sourceObject as GenericObjectBase<int>;

            int newValue;
            if (buffer.data.Length == 2)
                newValue = buffer.data[1] & 0x3f;
            else
                newValue = buffer.data[2];

            if (newValue > 127)
                newValue -= 256;

            castObject.Value = newValue;
        }

        private void FillS16ObjectValue(ObjectBase sourceObject, tuwien.auto.eibclient.EIBBuffer buffer)
        {
            GenericObjectBase<int> castObject = sourceObject as GenericObjectBase<int>;

            int newValue = (buffer.data[2] << 8) | buffer.data[3];

            if (newValue > 32767)
                newValue -= 65536;

            castObject.Value = newValue;
        }

        private void FillS32ObjectValue(ObjectBase sourceObject, tuwien.auto.eibclient.EIBBuffer buffer)
        {
            GenericObjectBase<int> castObject = sourceObject as GenericObjectBase<int>;

            int newValue = (buffer.data[2] << 24) | (buffer.data[3] << 16) | (buffer.data[4] << 8) | buffer.data[5];
            castObject.Value = newValue;
        }

        private void FillUnsignedIntValue(ObjectBase sourceObject, tuwien.auto.eibclient.EIBBuffer buffer)
        {
            GenericObjectBase<uint> castObject = sourceObject as GenericObjectBase<uint>;
            if (buffer.data.Length == 2)
                castObject.Value = Convert.ToUInt32(buffer.data[1] & 0x3f);
            else
                castObject.Value = buffer.data[2];

        }

        private void FillDateObjectValue(ObjectBase sourceObject, tuwien.auto.eibclient.EIBBuffer buffer)
        {
            GenericObjectBase<DateTime> castObject = sourceObject as GenericObjectBase<DateTime>;
            if (buffer.data.Length < 5)
            {
                /// TODO
                /// CustomLogger.WriteErrorEntry("DateObject : Invalid packed received (too short)");
            }
            byte[] buf = buffer.data;
            int day, month, year;
            day = buf[2];
            month = buf[3];
            year = buf[4];

            if (year >= 90)
                year += 1900;
            else
                year += 2000;

            castObject.Value = new DateTime(year, month, day);

        }

        private void FillStepDirObjectValue(ObjectBase sourceObject, tuwien.auto.eibclient.EIBBuffer buffer)
        {
            GenericObjectBase<KNXObjects.Types.StepDirType> castObject = sourceObject as GenericObjectBase<KNXObjects.Types.StepDirType>;
            int len = buffer.data.Length;
            int newValue;
            if (len == 2)
                newValue = buffer.data[1] & 0x3f;
            else
                newValue = buffer.data[2];

            int direction = (newValue & 0x08) >> 3;
            int stepCode = newValue & 0x07;
            if (stepCode == 0)
                direction = 0;
            
            castObject.Value.StepCode = stepCode;
            castObject.Value.Direction = direction;
        }

        private void FillSwitchingObjectValue(ObjectBase sourceObject, tuwien.auto.eibclient.EIBBuffer buffer)
        {
            int len = buffer.data.Length;
            if (len == 2)
                ((SwitchingObject)sourceObject).Value = (buffer.data[1] & 0x3f) != 0;
            else
                ((SwitchingObject)sourceObject).Value = buffer.data[2] != 0;
        }

        #endregion

    }
}
